﻿using LeRenard.Core.Numbers;
using LeRenard.ScriptWars.Model.Entities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace LeRenard.ScriptWars.Model.Scripting.Definitions
{
    /// <summary>
    /// Base implementation of <see cref="IOperationDefinition"/>
    /// </summary>
    public class OperationDefinition : IOperationDefinition
    {
        #region Properties
        /// <summary>
        /// Gets the operation's name.
        /// </summary>
        public string Name { get { return _name; } }
        public readonly string _name;

        /// <summary>
        /// Gets the operation's parameters.
        /// </summary>
        public IReadOnlyDictionary<int, IOperationParameterDefinition> Parameters { get { return _parameters; } }
        private readonly IReadOnlyDictionary<int, IOperationParameterDefinition> _parameters;

        /// <summary>
        /// Gets the range of the parameters
        /// </summary>
        public IRange<int> ParameterRange { 
            get {
                if (_parameterRange == null 
                    && Parameters.Count != 0) {
                        _parameterRange = new Int32Range(1, Parameters.Count);
                }

                return _parameterRange;
            } 
        }
        private IRange<int> _parameterRange;


        /// <summary>
        /// Determines whether the operation has paremeters.
        /// </summary>
        public bool HasParameters {
            get {
                return Parameters.Count != 0;
            }
        }

        /// <summary>
        /// Gets the method/action to be excuted when <see cref="Execute"/> is called.
        /// </summary>
        public Action<IEntity, IAlgorithmCycler, IGame, IOperation> ExecutionMethod { get { return _executionMethod; } }
        private readonly Action<IEntity, IAlgorithmCycler, IGame, IOperation> _executionMethod;
        #endregion

        #region Construction
        /// <summary>
        /// Creates a new <see cref="OperationDefinition"/> object.
        /// </summary>
        /// <param name="name">The name of the operation. Will be capitalized.</param>
        /// <param name="parameters">The parameter(s), if any.</param>
        internal OperationDefinition(string name, Action<IEntity, IAlgorithmCycler, IGame, IOperation> executionMethod, IDictionary<int, IOperationParameterDefinition> parameters = null)
        {
            ThrowIf.NullOrEmpty(() => name);
            ThrowIf.Null(() => executionMethod);

            name = name.RemoveMultipleWhitespaces();
            if (name.Contains(" "))
                throw new ArgumentException("Parameter 'name' contains white spaces. An operation name can not contain one or more white spaces.", "name");
            if (name.Contains(","))
                throw new ArgumentException("Parameter 'name' contains white spaces. An operation name can not contain one or more commas.", "name");
            _name = name;

            if (parameters == null)
                parameters = new Dictionary<int, IOperationParameterDefinition>();
            _parameters = new ReadOnlyDictionary<int, IOperationParameterDefinition>(parameters);

            _executionMethod = executionMethod;
        }
        #endregion
    }
}
